package strategy;

import algorithm.Greenwave;
import algorithm.Kdalgorithm;
import com.openatc.core.common.IErrorEnumImplOuter;
import com.openatc.core.model.RESTRetBase;
import com.openatc.core.util.RESTRetUtils;
import com.openatc.model.model.Feature;
import com.openatc.model.model.Pattern;
import com.openatc.model.model.Phase;
import com.openatc.model.model.Split;
import com.openatc.optimize.fixedtimeplan.config.cross.CrossConfig;
import com.openatc.optimize.fixedtimeplan.config.cross.Ring;
import com.openatc.optimize.fixedtimeplan.model.control.Cross;
import com.openatc.optimize.fixedtimeplan.model.control.FixedtimePlan;
import model.RouteIntsection;
import model.RouteOpt;
import model.RoutePara;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.logging.Logger;

/**
 * @Classname RouteOptStrategy
 * @Description
 * @Date 2022/5/29 17:44
 * @Created by panleilei
 */
public class RouteOptStrategy {

    private Kdalgorithm kdalgorithm = new Kdalgorithm();

    private Logger logger = Logger.getLogger(RouteOptStrategy.class.getSimpleName());

    /**
     * 红波协调
     * @param routePara
     * @return
     */
    public RESTRetBase getRedRouteOpt(RoutePara routePara) {
        List<RouteIntsection> devs = routePara.getDevs();
        // 没有协调路口
        if (CollectionUtils.isEmpty(devs)){
            return RESTRetUtils.errorObj(IErrorEnumImplOuter.E_5003);
        }

        RouteOpt routeOpt = new RouteOpt();
        routeOpt.setDevs(devs);

        String type = routePara.getDirection(); // 上行/下行

        try {
            int offsets[] = new int[routePara.getDevs().size()]; // 保存优化后的相位差
            int intssplit[] = new int[routePara.getDevs().size()]; // 保存路口协调相位绿信比的值
            int intsdistances[] = new int[routePara.getDevs().size()]; // 保存各路口的距离
            // 获取关键路口的周期
            int keyCycle = calKeyIntersectionCycle(routePara.getKeyintsid(),devs);
            // 初始化各路口协调相位绿信比
            for (RouteIntsection dev : devs) {
                // 优化各路口周期
                cycleopt(keyCycle,dev.getFeature());
                // 保存各路口相位差
                offsets[dev.getSortid() - 1] = dev.getFeature().getPatternList().get(0).getOffset();
                // 保存各路口距离
                intsdistances[dev.getSortid() - 1] = dev.getDistance();
                // 协调相位号
                int phaseno;
                if (type.equals("up")) {
                    phaseno = dev.getForwardphaseid();
                } else {
                    phaseno = dev.getBackphaseid();
                }

                List<List<Split>> rings = dev.getFeature().getPatternList().get(0).getRings();
                // 获取参与协调相位的绿信比
                label:
                for (int i = 0; i < rings.size(); i++) {
                    for (Split split : rings.get(i)) {
                        if (phaseno == split.getId()) {
                            intssplit[dev.getSortid() - 1] = split.getValue();
                            break label;
                        }
                    }
                }
            }
            List<RouteIntsection> routeOptList = routeOpt.getDevs();

            // 协调速度
            double speed = calCoordinationSpeed(routePara);
            // 计算每个路口的相位差
            // 上行
            if (type.equals("up")) {
                for (RouteIntsection dev : routeOptList) {
                    int distance = dev.getDistance();
                    offsets = kdalgorithm.redwaveOffsetOptUp(distance, speed, intssplit, keyCycle, dev.getSortid(), offsets);
                }
            }
            // 下行
            else {
                Collections.reverse(routeOptList);
                for (RouteIntsection dev : routeOptList) {
                    offsets = kdalgorithm.redwaveOffsetOptDown(speed, intssplit, keyCycle, dev.getSortid(), offsets, intsdistances);
                }
                Collections.reverse(routeOptList);
            }

            for (RouteIntsection dev : routeOptList) {
                // 改变路口相位差
                dev.getFeature().getPatternList().get(0).setOffset(offsets[dev.getSortid() - 1]);
            }
            // 获取Wave
            Greenwave greenwave;
            int width = kdalgorithm.getMotorcadeWidth(intssplit);
            if (routePara.getDirection().equals("up")) {
                greenwave = kdalgorithm.getMUp(type, speed, width);
            } else {
                greenwave = kdalgorithm.getMDown(type, speed, width);
            }
            List<Greenwave> greenwaves = new LinkedList<>();

            greenwaves.add(greenwave);

            routeOpt.setGreenwave(greenwaves);
            return RESTRetUtils.successObj(routeOpt);
        } catch (Exception e){
            logger.info(e.getMessage());
            return RESTRetUtils.errorObj(IErrorEnumImplOuter.E_5003);
        }
    }

    /**
     * 计算关键路口的周期
     * @param keyintsid
     * @param devs
     * @return
     */
    private int calKeyIntersectionCycle(String keyintsid, List<RouteIntsection> devs) {
       return devs.stream().filter(
                dev -> keyintsid.equals(dev.getAgentid())).findFirst().get().getFeature().getPatternList().get(0).getCycle();
    }

    /**
     * 计算协调速度
     * @param routePara
     * @return
     */
    private double calCoordinationSpeed(RoutePara routePara) {
        // 协调方向
        String type = routePara.getDirection();
        // 上行
        if ("up".equals(type)){
            return routePara.getUpspeed();
        }
        // 下行
        else {
            return routePara.getDownspeed();
        }
    }

    /**
     * 判断周期长度是否一致
     * @param intscycle
     * @return
     */
    private boolean isAllCycleEqual(int[] intscycle) {
        if (intscycle.length == 0){
            return false;
        }
        int firstElement = intscycle[0];
        for (int i : intscycle){
            if (firstElement != i){
                return false;
            }
        }
        return true;
    }

    /**
     * 车队尾协调策略
     * @param routePara
     * @return
     */
    public RESTRetBase getLastOfMotorcadeRouteOpt(RoutePara routePara) {
        List<RouteIntsection> devs = routePara.getDevs();
        if (CollectionUtils.isEmpty(devs)) {
            return RESTRetUtils.errorObj(IErrorEnumImplOuter.E_5003);
        }

        RouteOpt routeOpt = new RouteOpt();
        routeOpt.setDevs(routePara.getDevs());

        try {
            int offsets[] = new int[routePara.getDevs().size()]; // 保存优化后的相位差
            int intssplit[] = new int[routePara.getDevs().size()]; // 保存路口协调相位绿信比的值
            int intsdistances[] = new int[routePara.getDevs().size()]; // 保存各路口的距离
            int keyCycle = calKeyIntersectionCycle(routePara.getKeyintsid(), devs);
            // 初始化各路口协调相位绿信比
            for (RouteIntsection dev : devs) {
                cycleopt(keyCycle,dev.getFeature());
                // 保存周期
                offsets[dev.getSortid() - 1] = dev.getFeature().getPatternList().get(0).getOffset();
                // 保存各路口距离
                intsdistances[dev.getSortid() - 1] = dev.getDistance();
                // 协调相位号
                int phaseno;
                if (routePara.getDirection().equals("up")) {
                    phaseno = dev.getForwardphaseid();
                } else {
                    phaseno = dev.getBackphaseid();
                }

                List<List<Split>> rings = dev.getFeature().getPatternList().get(0).getRings();
                // 获取参与协调相位的绿信比
                label:
                for (int i = 0; i < rings.size(); i++) {
                    for (Split split : rings.get(i)) {
                        if (phaseno == split.getId()) {
                            intssplit[dev.getSortid() - 1] = split.getValue();
                            break label;
                        }
                    }
                }
            }

            List<RouteIntsection> routeOptList = routeOpt.getDevs();

            // 协调速度
            double speed = calCoordinationSpeed(routePara);
            // 计算每个路口的相位差
            // 上行
            if (routePara.getDirection().equals("up")) {
                for (RouteIntsection dev : routeOptList) {
                    int distance = dev.getDistance();
                    offsets = kdalgorithm.motorcadeOffsetOptUp(distance, speed, intssplit, keyCycle, dev.getSortid(), offsets);
                }
            }
            // 下行
            else {
                Collections.reverse(routeOptList);
                for (RouteIntsection dev : routeOptList) {
                    offsets = kdalgorithm.motorcadeOffsetOptDown(speed, intssplit, keyCycle, dev.getSortid(), offsets, intsdistances);
                }
                Collections.reverse(routeOptList);
            }

            for (RouteIntsection dev : routeOptList) {
                // 改变路口相位差
                dev.getFeature().getPatternList().get(0).setOffset(offsets[dev.getSortid() - 1]);
            }
            // 获取Wave
            String type = routePara.getDirection(); // 上行/下行
            Greenwave greenwave;
            int width = kdalgorithm.getMotorcadeWidth(intssplit);
            if (routePara.getDirection().equals("up")) {
                greenwave = kdalgorithm.getMUp(type, speed, width);
            } else {
                greenwave = kdalgorithm.getMDown(type, speed, width);
            }
            List<Greenwave> greenwaves = new LinkedList<>();
            greenwaves.add(greenwave);

            routeOpt.setGreenwave(greenwaves);
            return RESTRetUtils.successObj(routeOpt);
        } catch (Exception e){
            logger.info(e.toString());
            return RESTRetUtils.errorObj(IErrorEnumImplOuter.E_5003);
        }
    }

    /**
     * 绿波协调
     * @param routePara
     * @return
     */
    public RESTRetBase getGreenRouteOpt(RoutePara routePara) {

        //计算优化
        double intslenth[] = new double[routePara.getDevs().size()]; //保存路口长度的值
        int intssplit[] = new int[routePara.getDevs().size()]; //保存路口协调相位绿信比的值

        int intsoffset[] = new int[routePara.getDevs().size()]; //保存优化后各路口的相位差的值
        int intsabs[] = new int[routePara.getDevs().size()];//各路口协调相位所属周期时间位置，用来计算绝对相位差
        int intscycle[] = new int[routePara.getDevs().size()];  // 保存各路口周期长度

        try {
            double intsvelup = 0.00;
            double intsveldown = 0.00;

            intsvelup = routePara.getUpspeed();
            intsveldown = routePara.getDownspeed();

            List<RouteIntsection> deviceList = routePara.getDevs();

            String keyAgentid = routePara.getKeyintsid();
            int keyCycle = 0;
            // 得到关键路口的周期
            for (RouteIntsection device : deviceList) {
                if( keyAgentid.equals( device.getAgentid() ) ){
                    keyCycle = device.getFeature().getPatternList().get(0).getCycle();
                    break;
                }
            }
            if( keyCycle == 0 )
                return RESTRetUtils.errorObj(IErrorEnumImplOuter.E_5002);

            // 优化各路口方案
            int phaseno = 0;
            for (RouteIntsection device : deviceList) {
                Pattern pattern = device.getFeature().getPatternList().get(0);
                // 优化各路口周期
                cycleopt(keyCycle, device.getFeature());
                // 保存各路口周期
                intscycle[device.getSortid() - 1] = device.getFeature().getPatternList().get(0).getCycle();
                //得到距离
                intslenth[device.getSortid() - 1] = device.getDistance();

                //得到周期
                int cycle = device.getFeature().getPatternList().get(0).getCycle();
                if (cycle == 0)
                    return null;
                //根据相位号得到协调相位值
                if (routePara.getDirection().equals("up"))
                    phaseno = device.getForwardphaseid();
                else if (routePara.getDirection().equals("down"))
                    phaseno = device.getBackphaseid();
                else
                    phaseno = device.getForwardphaseid();

                List<List<Split>> rings = device.getFeature().getPatternList().get(0).getRings();

                label:
                for (int i = 0; i < rings.size(); i++) {
                    List<Split> ring = rings.get(i);
                    for (Split split : ring) {
                        if (split.getId() == phaseno) {
                            intssplit[device.getSortid() - 1] = split.getValue();
                            break label;
                        } else {
                            intsabs[device.getSortid() - 1] += split.getValue();
                        }
                    }
                }

                while (intsabs[device.getSortid() - 1] >= cycle) {
                    intsabs[device.getSortid() - 1] -= cycle;
                }
            }

            boolean allCycleEqual = isAllCycleEqual(intscycle);
            if (!allCycleEqual)
//                return null;
                return RESTRetUtils.errorObj(IErrorEnumImplOuter.E_5002);
            kdalgorithm.setAftcycle(keyCycle);
            if (routePara.getDirection().equals("up"))
                intsoffset = kdalgorithm.offsetOpt(routePara.getDirection(), intslenth, intsvelup, intssplit);
            else if (routePara.getDirection().equals("down"))
                intsoffset = kdalgorithm.offsetOpt(routePara.getDirection(), intslenth, intsveldown, intssplit);
            else
                intsoffset = kdalgorithm.offsetByBiDirection(intslenth, intsvelup, intsveldown, intssplit);

            RouteOpt routeOpt = new RouteOpt();
            routeOpt.setDevs(routePara.getDevs());
            List<RouteIntsection> routeOptList = routeOpt.getDevs();

            for (RouteIntsection device : routeOptList) {

                //把每个路口相位差转成绝对相位差，start位置也会变化
                intsoffset[device.getSortid() - 1] -= intsabs[device.getSortid() - 1];
                if (intsoffset[device.getSortid() - 1] < 0)
                    intsoffset[device.getSortid() - 1] += keyCycle;

                device.getFeature().getPatternList().get(0).setOffset(intsoffset[device.getSortid() - 1]);
            }

            // 计算绿波带宽
            routeOpt.setGreenwave( getBandWidth(null,intsvelup, intsveldown, routeOptList ) );

            return RESTRetUtils.successObj(routeOpt);
        } catch (Exception e) {
            logger.info(e.toString());
            return RESTRetUtils.errorObj(IErrorEnumImplOuter.E_5003);
        }
    }

    /**
     * 获取干线协调带宽
     * @param type 计算带宽类型，green - 绿波带宽， red - 红波带宽
     * @param intsvelup 上行速度
     * @param intsveldown 下行速度
     * @param devs 路口参数
     * @return
     */
    public List<Greenwave> getBandWidth(String type,double intsvelup, double intsveldown,  List<RouteIntsection> devs){

        // todo: 完善红波带宽计算
        type = "green";

        int intsoffset[] = new int[devs.size()]; // 保存优化后的相位差
        int intssplitup[] = new int[devs.size()]; // 保存路口协调相位绿信比的值
        int intssplitdown[] = new int[devs.size()]; // 保存路口协调相位绿信比的值

        int intscycle[] = new int[devs.size()]; // 保存协调路口方案的周期
//        int intsdistances[] = new int[devs.size()]; // 保存各路口的距离

        // 初始化各路口协调相位绿信比
        for (RouteIntsection dev : devs) {
            // 保存周期
            intscycle[dev.getSortid() - 1] = dev.getFeature().getPatternList().get(0).getCycle();
            // 保存各路口相位差
            intsoffset[dev.getSortid() - 1] = dev.getFeature().getPatternList().get(0).getOffset();
            // 保存各路口距离
//            intsdistances[dev.getSortid() - 1] = dev.getDistance();
            // 协调相位号
            int phasenoUp;
            phasenoUp = dev.getForwardphaseid();
            int phasenoDown;
            phasenoDown = dev.getBackphaseid();

            List<List<Split>> rings = dev.getFeature().getPatternList().get(0).getRings();
            // 获取参与协调相位的绿信比
            label:
            for (int i = 0; i < rings.size(); i++) {
                for (Split split : rings.get(i)) {
                    if (phasenoUp == split.getId()) {
                        intssplitup[dev.getSortid() - 1] = split.getValue();
                        break label;
                    }
                }
            }
            label:
            for (int i = 0; i < rings.size(); i++) {
                for (Split split : rings.get(i)) {
                    if (phasenoDown == split.getId()) {
                        intssplitdown[dev.getSortid() - 1] = split.getValue();
                        break label;
                    }
                }
            }
        }

        kdalgorithm.calbandwidth("up",intsvelup, intssplitup,intsoffset );
        kdalgorithm.calbandwidth("down",intsveldown, intssplitdown,intsoffset );

        List<Greenwave> greenwaveList = new LinkedList<>();
        greenwaveList.add(kdalgorithm.getGwup());
        greenwaveList.add(kdalgorithm.getGwdown());

        return greenwaveList;
    }

    private void cycleopt(int keyCycle, Feature feature) {
        Pattern pattern = feature.getPatternList().get(0);
        List<Phase> phase = feature.getPhaseList();

        CrossConfig crossConfig = new CrossConfig();
        crossConfig.setType("cycle-opt");
        crossConfig.setOptcycle(keyCycle);
        crossConfig.setPattern(pattern);
        crossConfig.setPhaseList(phase);

        crossConfig.init();
        Cross cross = Cross.builder()
                .agentid(crossConfig.getAgentid())
                .crossConfig(crossConfig)
                .phaseMap(new HashMap<>())
                .build();
        cross.updatePhaseMap();

        FixedtimePlan fixedtimePlan = cross.optimize();
        int ringnum = 0;
        for(Ring ring : fixedtimePlan.getRings() ){
            int phasenum = 0;
            for ( Integer id : ring.getSequence() ){
                pattern.getRings().get(ringnum).get(phasenum).setValue((int) cross.getPhaseMap().get(id).getDuration());
                phasenum++;
            }
            ringnum++;
        }
        pattern.setCycle(keyCycle);
    }



}
